{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configurations for Colab"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "IN_COLAB = 'google.colab' in sys.modules\n",
    "\n",
    "if IN_COLAB:\n",
    "    !apt-get install -y xvfb python-opengl > /dev/null 2>&1\n",
    "    !pip install gym pyvirtualdisplay > /dev/null 2>&1\n",
    "    !pip install JSAnimation==0.1\n",
    "    \n",
    "    from pyvirtualdisplay import Display\n",
    "    \n",
    "    # Start virtual display\n",
    "    dis = Display(visible=0, size=(400, 400))\n",
    "    dis.start()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 04. Dueling Network\n",
    "\n",
    "[Z. Wang et al., \"Dueling Network Architectures for Deep Reinforcement Learning.\" arXiv preprint arXiv:1511.06581, 2015.](https://arxiv.org/pdf/1511.06581.pdf)\n",
    "\n",
    "The proposed network architecture, which is named *dueling architecture*, explicitly separates the representation of state values and (state-dependent) action advantages. \n",
    "\n",
    "![fig1](https://user-images.githubusercontent.com/14961526/60322956-c2f0b600-99bb-11e9-9ed4-443bd14bc3b0.png)\n",
    "\n",
    "The dueling network automatically produces separate estimates of the state value function and advantage function, without any extra supervision. Intuitively, the dueling architecture can learn which states are (or are not) valuable, without having to learn the effect of each action for each state. This is particularly useful in states where its actions do not affect the environment in any relevant way. \n",
    "\n",
    "The dueling architecture represents both the value $V(s)$ and advantage $A(s, a)$ functions with a single deep model whose output combines the two to produce a state-action value $Q(s, a)$. Unlike in advantage updating, the representation and algorithm are decoupled by construction.\n",
    "\n",
    "$$A^\\pi (s, a) = Q^\\pi (s, a) - V^\\pi (s).$$\n",
    "\n",
    "The value function $V$ measures the how good it is to be in a particular state $s$. The $Q$ function, however, measures the the value of choosing a particular action when in this state. Now, using the definition of advantage, we might be tempted to construct the aggregating module as follows:\n",
    "\n",
    "$$Q(s, a; \\theta, \\alpha, \\beta) = V (s; \\theta, \\beta) + A(s, a; \\theta, \\alpha),$$\n",
    "\n",
    "where $\\theta$ denotes the parameters of the convolutional layers, while $\\alpha$ and $\\beta$ are the parameters of the two streams of fully-connected layers.\n",
    "\n",
    "Unfortunately, the above equation is unidentifiable in the sense that given $Q$ we cannot recover $V$ and $A$ uniquely; for example, there are uncountable pairs of $V$ and $A$ that make $Q$ values to zero. To address this issue of identifiability, we can force the advantage function estimator to have zero advantage at the chosen action. That is, we let the last module of the network implement the forward mapping.\n",
    "\n",
    "$$\n",
    "Q(s, a; \\theta, \\alpha, \\beta) = V (s; \\theta, \\beta) + \\big( A(s, a; \\theta, \\alpha) - \\max_{a' \\in |\\mathcal{A}|} A(s, a'; \\theta, \\alpha) \\big).\n",
    "$$\n",
    "\n",
    "This formula guarantees that we can recover the unique $V$ and $A$, but the optimization is not so stable because the advantages have to compensate any change to the optimal action’s advantage. Due to the reason, an alternative module that replaces the max operator with an average is proposed:\n",
    "\n",
    "$$\n",
    "Q(s, a; \\theta, \\alpha, \\beta) = V (s; \\theta, \\beta) + \\big( A(s, a; \\theta, \\alpha) - \\frac{1}{|\\mathcal{A}|} \\sum_{a'} A(s, a'; \\theta, \\alpha) \\big).\n",
    "$$\n",
    "\n",
    "Unlike the max advantage form, in this formula, the advantages only need to change as fast as the mean, so it increases the stability of optimization."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from typing import Dict, List, Tuple\n",
    "\n",
    "import gym\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "from IPython.display import clear_output\n",
    "from torch.nn.utils import clip_grad_norm_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Replay buffer\n",
    "\n",
    "Please see *01.dqn.ipynb* for detailed description."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ReplayBuffer:\n",
    "    \"\"\"A simple numpy replay buffer.\"\"\"\n",
    "\n",
    "    def __init__(self, obs_dim: int, size: int, batch_size: int = 32):\n",
    "        self.obs_buf = np.zeros([size, obs_dim], dtype=np.float32)\n",
    "        self.next_obs_buf = np.zeros([size, obs_dim], dtype=np.float32)\n",
    "        self.acts_buf = np.zeros([size], dtype=np.float32)\n",
    "        self.rews_buf = np.zeros([size], dtype=np.float32)\n",
    "        self.done_buf = np.zeros(size, dtype=np.float32)\n",
    "        self.max_size, self.batch_size = size, batch_size\n",
    "        self.ptr, self.size, = 0, 0\n",
    "\n",
    "    def store(\n",
    "        self,\n",
    "        obs: np.ndarray,\n",
    "        act: np.ndarray, \n",
    "        rew: float, \n",
    "        next_obs: np.ndarray, \n",
    "        done: bool,\n",
    "    ):\n",
    "        self.obs_buf[self.ptr] = obs\n",
    "        self.next_obs_buf[self.ptr] = next_obs\n",
    "        self.acts_buf[self.ptr] = act\n",
    "        self.rews_buf[self.ptr] = rew\n",
    "        self.done_buf[self.ptr] = done\n",
    "        self.ptr = (self.ptr + 1) % self.max_size\n",
    "        self.size = min(self.size + 1, self.max_size)\n",
    "\n",
    "    def sample_batch(self) -> Dict[str, np.ndarray]:\n",
    "        idxs = np.random.choice(self.size, size=self.batch_size, replace=False)\n",
    "        return dict(obs=self.obs_buf[idxs],\n",
    "                    next_obs=self.next_obs_buf[idxs],\n",
    "                    acts=self.acts_buf[idxs],\n",
    "                    rews=self.rews_buf[idxs],\n",
    "                    done=self.done_buf[idxs])\n",
    "\n",
    "    def __len__(self) -> int:\n",
    "        return self.size"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dueling Network\n",
    "\n",
    "Carefully take a look at advantage and value layers separated from feature layer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Network(nn.Module):\n",
    "    def __init__(self, in_dim: int, out_dim: int):\n",
    "        \"\"\"Initialization.\"\"\"\n",
    "        super(Network, self).__init__()\n",
    "\n",
    "        # set common feature layer\n",
    "        self.feature_layer = nn.Sequential(\n",
    "            nn.Linear(in_dim, 128), \n",
    "            nn.ReLU(),\n",
    "        )\n",
    "        \n",
    "        # set advantage layer\n",
    "        self.advantage_layer = nn.Sequential(\n",
    "            nn.Linear(128, 128),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(128, out_dim),\n",
    "        )\n",
    "\n",
    "        # set value layer\n",
    "        self.value_layer = nn.Sequential(\n",
    "            nn.Linear(128, 128),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(128, 1),\n",
    "        )\n",
    "\n",
    "    def forward(self, x: torch.Tensor) -> torch.Tensor:\n",
    "        \"\"\"Forward method implementation.\"\"\"\n",
    "        feature = self.feature_layer(x)\n",
    "        \n",
    "        value = self.value_layer(feature)\n",
    "        advantage = self.advantage_layer(feature)\n",
    "\n",
    "        q = value + advantage - advantage.mean(dim=-1, keepdim=True)\n",
    "        \n",
    "        return q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DQN + DuelingNet Agent (w/o Double-DQN & PER)\n",
    "\n",
    "Here is a summary of DQNAgent class.\n",
    "\n",
    "| Method           | Note                                                 |\n",
    "| ---              | ---                                                  |\n",
    "|select_action     | select an action from the input state.               |\n",
    "|step              | take an action and return the response of the env.   |\n",
    "|compute_dqn_loss  | return dqn loss.                                     |\n",
    "|update_model      | update the model by gradient descent.                |\n",
    "|target_hard_update| hard update from the local model to the target model.|\n",
    "|train             | train the agent during num_frames.                   |\n",
    "|test              | test the agent (1 episode).                          |\n",
    "|plot              | plot the training progresses.                        |\n",
    "\n",
    "\n",
    "Aside from the dueling network architecture, the authors suggest to use Double-DQN and Prioritized Experience Replay as extra components for better performance. However, we don't implement them to simplify the tutorial. Here, DQNAgent is totally same as the one from *01.dqn.ipynb*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class DQNAgent:\n",
    "    \"\"\"DQN Agent interacting with environment.\n",
    "    \n",
    "    Attribute:\n",
    "        env (gym.Env): openAI Gym environment\n",
    "        memory (ReplayBuffer): replay memory to store transitions\n",
    "        batch_size (int): batch size for sampling\n",
    "        epsilon (float): parameter for epsilon greedy policy\n",
    "        epsilon_decay (float): step size to decrease epsilon\n",
    "        max_epsilon (float): max value of epsilon\n",
    "        min_epsilon (float): min value of epsilon\n",
    "        target_update (int): period for target model's hard update\n",
    "        gamma (float): discount factor\n",
    "        dqn (Network): model to train and select actions\n",
    "        dqn_target (Network): target model to update\n",
    "        optimizer (torch.optim): optimizer for training dqn\n",
    "        transition (list): transition information including\n",
    "                           state, action, reward, next_state, done\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(\n",
    "        self, \n",
    "        env: gym.Env,\n",
    "        memory_size: int,\n",
    "        batch_size: int,\n",
    "        target_update: int,\n",
    "        epsilon_decay: float,\n",
    "        max_epsilon: float = 1.0,\n",
    "        min_epsilon: float = 0.1,\n",
    "        gamma: float = 0.99,\n",
    "    ):\n",
    "        \"\"\"Initialization.\n",
    "        \n",
    "        Args:\n",
    "            env (gym.Env): openAI Gym environment\n",
    "            memory_size (int): length of memory\n",
    "            batch_size (int): batch size for sampling\n",
    "            target_update (int): period for target model's hard update\n",
    "            epsilon_decay (float): step size to decrease epsilon\n",
    "            lr (float): learning rate\n",
    "            max_epsilon (float): max value of epsilon\n",
    "            min_epsilon (float): min value of epsilon\n",
    "            gamma (float): discount factor\n",
    "        \"\"\"\n",
    "        obs_dim = env.observation_space.shape[0]\n",
    "        action_dim = env.action_space.n\n",
    "        \n",
    "        self.env = env\n",
    "        self.memory = ReplayBuffer(obs_dim, memory_size, batch_size)\n",
    "        self.batch_size = batch_size\n",
    "        self.epsilon = max_epsilon\n",
    "        self.epsilon_decay = epsilon_decay\n",
    "        self.max_epsilon = max_epsilon\n",
    "        self.min_epsilon = min_epsilon\n",
    "        self.target_update = target_update\n",
    "        self.gamma = gamma\n",
    "        \n",
    "        # device: cpu / gpu\n",
    "        self.device = torch.device(\n",
    "            \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
    "        )\n",
    "        print(self.device)\n",
    "\n",
    "        # networks: dqn, dqn_target\n",
    "        self.dqn = Network(obs_dim, action_dim).to(self.device)\n",
    "        self.dqn_target = Network(obs_dim, action_dim).to(self.device)\n",
    "        self.dqn_target.load_state_dict(self.dqn.state_dict())\n",
    "        self.dqn_target.eval()\n",
    "        \n",
    "        # optimizer\n",
    "        self.optimizer = optim.Adam(self.dqn.parameters())\n",
    "\n",
    "        # transition to store in memory\n",
    "        self.transition = list()\n",
    "        \n",
    "        # mode: train / test\n",
    "        self.is_test = False\n",
    "\n",
    "    def select_action(self, state: np.ndarray) -> np.ndarray:\n",
    "        \"\"\"Select an action from the input state.\"\"\"\n",
    "        # epsilon greedy policy\n",
    "        if self.epsilon > np.random.random():\n",
    "            selected_action = self.env.action_space.sample()\n",
    "        else:\n",
    "            selected_action = self.dqn(\n",
    "                torch.FloatTensor(state).to(self.device)\n",
    "            ).argmax()\n",
    "            selected_action = selected_action.detach().cpu().numpy()\n",
    "        \n",
    "        if not self.is_test:\n",
    "            self.transition = [state, selected_action]\n",
    "        \n",
    "        return selected_action\n",
    "\n",
    "    def step(self, action: np.ndarray) -> Tuple[np.ndarray, np.float64, bool]:\n",
    "        \"\"\"Take an action and return the response of the env.\"\"\"\n",
    "        next_state, reward, done, _ = self.env.step(action)\n",
    "\n",
    "        if not self.is_test:\n",
    "            self.transition += [reward, next_state, done]\n",
    "            self.memory.store(*self.transition)\n",
    "    \n",
    "        return next_state, reward, done\n",
    "\n",
    "    def update_model(self) -> torch.Tensor:\n",
    "        \"\"\"Update the model by gradient descent.\"\"\"\n",
    "        samples = self.memory.sample_batch()\n",
    "\n",
    "        loss = self._compute_dqn_loss(samples)\n",
    "\n",
    "        self.optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        # gradient clipping\n",
    "        # https://pytorch.org/docs/stable/nn.html#torch.nn.utils.clip_grad_norm_\n",
    "        clip_grad_norm_(self.dqn.parameters(), 1.0, norm_type=1)\n",
    "        self.optimizer.step()\n",
    "\n",
    "        return loss.item()\n",
    "        \n",
    "    def train(self, num_frames: int, plotting_interval: int = 200):\n",
    "        \"\"\"Train the agent.\"\"\"\n",
    "        self.is_test = False\n",
    "        \n",
    "        state = self.env.reset()\n",
    "        update_cnt = 0\n",
    "        epsilons = []\n",
    "        losses = []\n",
    "        scores = []\n",
    "        score = 0\n",
    "\n",
    "        for frame_idx in range(1, num_frames + 1):\n",
    "            action = self.select_action(state)\n",
    "            next_state, reward, done = self.step(action)\n",
    "\n",
    "            state = next_state\n",
    "            score += reward\n",
    "\n",
    "            # if episode ends\n",
    "            if done:\n",
    "                state = env.reset()\n",
    "                scores.append(score)\n",
    "                score = 0\n",
    "\n",
    "            # if training is ready\n",
    "            if len(self.memory) >= self.batch_size:\n",
    "                loss = self.update_model()\n",
    "                losses.append(loss)\n",
    "                update_cnt += 1\n",
    "                \n",
    "                # linearly decrease epsilon\n",
    "                self.epsilon = max(\n",
    "                    self.min_epsilon, self.epsilon - (\n",
    "                        self.max_epsilon - self.min_epsilon\n",
    "                    ) * self.epsilon_decay\n",
    "                )\n",
    "                epsilons.append(self.epsilon)\n",
    "                \n",
    "                # if hard update is needed\n",
    "                if update_cnt % self.target_update == 0:\n",
    "                    self._target_hard_update()\n",
    "\n",
    "            # plotting\n",
    "            if frame_idx % plotting_interval == 0:\n",
    "                self._plot(frame_idx, scores, losses, epsilons)\n",
    "                \n",
    "        self.env.close()\n",
    "                \n",
    "    def test(self) -> List[np.ndarray]:\n",
    "        \"\"\"Test the agent.\"\"\"\n",
    "        self.is_test = True\n",
    "        \n",
    "        state = self.env.reset()\n",
    "        done = False\n",
    "        score = 0\n",
    "        \n",
    "        frames = []\n",
    "        while not done:\n",
    "            frames.append(self.env.render(mode=\"rgb_array\"))\n",
    "            action = self.select_action(state)\n",
    "            next_state, reward, done = self.step(action)\n",
    "\n",
    "            state = next_state\n",
    "            score += reward\n",
    "        \n",
    "        print(\"score: \", score)\n",
    "        self.env.close()\n",
    "        \n",
    "        return frames\n",
    "\n",
    "    def _compute_dqn_loss(self, samples: Dict[str, np.ndarray]) -> torch.Tensor:\n",
    "        \"\"\"Return dqn loss.\"\"\"\n",
    "        device = self.device  # for shortening the following lines\n",
    "        state = torch.FloatTensor(samples[\"obs\"]).to(device)\n",
    "        next_state = torch.FloatTensor(samples[\"next_obs\"]).to(device)\n",
    "        action = torch.LongTensor(samples[\"acts\"].reshape(-1, 1)).to(device)\n",
    "        reward = torch.FloatTensor(samples[\"rews\"].reshape(-1, 1)).to(device)\n",
    "        done = torch.FloatTensor(samples[\"done\"].reshape(-1, 1)).to(device)\n",
    "\n",
    "        # G_t   = r + gamma * v(s_{t+1})  if state != Terminal\n",
    "        #       = r                       otherwise\n",
    "        curr_q_value = self.dqn(state).gather(1, action)\n",
    "        next_q_value = self.dqn_target(next_state).max(\n",
    "            dim=1, keepdim=True\n",
    "        )[0].detach()\n",
    "        mask = 1 - done\n",
    "        target = (reward + self.gamma * next_q_value * mask).to(self.device)\n",
    "\n",
    "        # calculate dqn loss\n",
    "        loss = ((target - curr_q_value).pow(2)).mean()\n",
    "\n",
    "        return loss\n",
    "\n",
    "    def _target_hard_update(self):\n",
    "        \"\"\"Hard update: target <- local.\"\"\"\n",
    "        self.dqn_target.load_state_dict(self.dqn.state_dict())\n",
    "                \n",
    "    def _plot(\n",
    "        self, \n",
    "        frame_idx: int, \n",
    "        scores: List[float], \n",
    "        losses: List[float], \n",
    "        epsilons: List[float],\n",
    "    ):\n",
    "        \"\"\"Plot the training progresses.\"\"\"\n",
    "        clear_output(True)\n",
    "        plt.figure(figsize=(20, 5))\n",
    "        plt.subplot(131)\n",
    "        plt.title('frame %s. score: %s' % (frame_idx, np.mean(scores[-10:])))\n",
    "        plt.plot(scores)\n",
    "        plt.subplot(132)\n",
    "        plt.title('loss')\n",
    "        plt.plot(losses)\n",
    "        plt.subplot(133)\n",
    "        plt.title('epsilons')\n",
    "        plt.plot(epsilons)\n",
    "        plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Environment\n",
    "\n",
    "You can see the [code](https://github.com/openai/gym/blob/master/gym/envs/classic_control/cartpole.py) and [configurations](https://github.com/openai/gym/blob/master/gym/envs/__init__.py#L53) of CartPole-v0 from OpenAI's repository."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# environment\n",
    "env_id = \"CartPole-v0\"\n",
    "env = gym.make(env_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set random seed"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[777]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "seed = 777\n",
    "\n",
    "def seed_torch(seed):\n",
    "    torch.manual_seed(seed)\n",
    "    if torch.backends.cudnn.enabled:\n",
    "        torch.backends.cudnn.benchmark = False\n",
    "        torch.backends.cudnn.deterministic = True\n",
    "\n",
    "np.random.seed(seed)\n",
    "seed_torch(seed)\n",
    "env.seed(seed)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Initialize"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cuda\n"
     ]
    }
   ],
   "source": [
    "# parameters\n",
    "num_frames = 10000\n",
    "memory_size = 1000\n",
    "batch_size = 32\n",
    "target_update = 200\n",
    "epsilon_decay = 1 / 2000\n",
    "\n",
    "# train\n",
    "agent = DQNAgent(env, memory_size, batch_size, target_update, epsilon_decay)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Train"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1440x360 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "agent.train(num_frames)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test\n",
    "\n",
    "Run the trained agent (1 episode)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "score:  200.0\n"
     ]
    }
   ],
   "source": [
    "frames = agent.test()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Render"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<script language=\"javascript\">\n",
       "  /* Define the Animation class */\n",
       "  function Animation(frames, img_id, slider_id, interval, loop_select_id){\n",
       "    this.img_id = img_id;\n",
       "    this.slider_id = slider_id;\n",
       "    this.loop_select_id = loop_select_id;\n",
       "    this.interval = interval;\n",
       "    this.current_frame = 0;\n",
       "    this.direction = 0;\n",
       "    this.timer = null;\n",
       "    this.frames = new Array(frames.length);\n",
       "\n",
       "    for (var i=0; i<frames.length; i++)\n",
       "    {\n",
       "     this.frames[i] = new Image();\n",
       "     this.frames[i].src = frames[i];\n",
       "    }\n",
       "    document.getElementById(this.slider_id).max = this.frames.length - 1;\n",
       "    this.set_frame(this.current_frame);\n",
       "  }\n",
       "\n",
       "  Animation.prototype.get_loop_state = function(){\n",
       "    var button_group = document[this.loop_select_id].state;\n",
       "    for (var i = 0; i < button_group.length; i++) {\n",
       "        var button = button_group[i];\n",
       "        if (button.checked) {\n",
       "            return button.value;\n",
       "        }\n",
       "    }\n",
       "    return undefined;\n",
       "  }\n",
       "\n",
       "  Animation.prototype.set_frame = function(frame){\n",
       "    this.current_frame = frame;\n",
       "    document.getElementById(this.img_id).src = this.frames[this.current_frame].src;\n",
       "    document.getElementById(this.slider_id).value = this.current_frame;\n",
       "  }\n",
       "\n",
       "  Animation.prototype.next_frame = function()\n",
       "  {\n",
       "    this.set_frame(Math.min(this.frames.length - 1, this.current_frame + 1));\n",
       "  }\n",
       "\n",
       "  Animation.prototype.previous_frame = function()\n",
       "  {\n",
       "    this.set_frame(Math.max(0, this.current_frame - 1));\n",
       "  }\n",
       "\n",
       "  Animation.prototype.first_frame = function()\n",
       "  {\n",
       "    this.set_frame(0);\n",
       "  }\n",
       "\n",
       "  Animation.prototype.last_frame = function()\n",
       "  {\n",
       "    this.set_frame(this.frames.length - 1);\n",
       "  }\n",
       "\n",
       "  Animation.prototype.slower = function()\n",
       "  {\n",
       "    this.interval /= 0.7;\n",
       "    if(this.direction > 0){this.play_animation();}\n",
       "    else if(this.direction < 0){this.reverse_animation();}\n",
       "  }\n",
       "\n",
       "  Animation.prototype.faster = function()\n",
       "  {\n",
       "    this.interval *= 0.7;\n",
       "    if(this.direction > 0){this.play_animation();}\n",
       "    else if(this.direction < 0){this.reverse_animation();}\n",
       "  }\n",
       "\n",
       "  Animation.prototype.anim_step_forward = function()\n",
       "  {\n",
       "    this.current_frame += 1;\n",
       "    if(this.current_frame < this.frames.length){\n",
       "      this.set_frame(this.current_frame);\n",
       "    }else{\n",
       "      var loop_state = this.get_loop_state();\n",
       "      if(loop_state == \"loop\"){\n",
       "        this.first_frame();\n",
       "      }else if(loop_state == \"reflect\"){\n",
       "        this.last_frame();\n",
       "        this.reverse_animation();\n",
       "      }else{\n",
       "        this.pause_animation();\n",
       "        this.last_frame();\n",
       "      }\n",
       "    }\n",
       "  }\n",
       "\n",
       "  Animation.prototype.anim_step_reverse = function()\n",
       "  {\n",
       "    this.current_frame -= 1;\n",
       "    if(this.current_frame >= 0){\n",
       "      this.set_frame(this.current_frame);\n",
       "    }else{\n",
       "      var loop_state = this.get_loop_state();\n",
       "      if(loop_state == \"loop\"){\n",
       "        this.last_frame();\n",
       "      }else if(loop_state == \"reflect\"){\n",
       "        this.first_frame();\n",
       "        this.play_animation();\n",
       "      }else{\n",
       "        this.pause_animation();\n",
       "        this.first_frame();\n",
       "      }\n",
       "    }\n",
       "  }\n",
       "\n",
       "  Animation.prototype.pause_animation = function()\n",
       "  {\n",
       "    this.direction = 0;\n",
       "    if (this.timer){\n",
       "      clearInterval(this.timer);\n",
       "      this.timer = null;\n",
       "    }\n",
       "  }\n",
       "\n",
       "  Animation.prototype.play_animation = function()\n",
       "  {\n",
       "    this.pause_animation();\n",
       "    this.direction = 1;\n",
       "    var t = this;\n",
       "    if (!this.timer) this.timer = setInterval(function(){t.anim_step_forward();}, this.interval);\n",
       "  }\n",
       "\n",
       "  Animation.prototype.reverse_animation = function()\n",
       "  {\n",
       "    this.pause_animation();\n",
       "    this.direction = -1;\n",
       "    var t = this;\n",
       "    if (!this.timer) this.timer = setInterval(function(){t.anim_step_reverse();}, this.interval);\n",
       "  }\n",
       "</script>\n",
       "\n",
       "<div class=\"animation\" align=\"center\">\n",
       "    <img id=\"_anim_imgPKLBSUBQGAJRUKQL\">\n",
       "    <br>\n",
       "    <input id=\"_anim_sliderPKLBSUBQGAJRUKQL\" type=\"range\" style=\"width:350px\" name=\"points\" min=\"0\" max=\"1\" step=\"1\" value=\"0\" onchange=\"animPKLBSUBQGAJRUKQL.set_frame(parseInt(this.value));\"></input>\n",
       "    <br>\n",
       "    <button onclick=\"animPKLBSUBQGAJRUKQL.slower()\">&#8211;</button>\n",
       "    <button onclick=\"animPKLBSUBQGAJRUKQL.first_frame()\"><img class=\"anim_icon\" src=\"\"></button>\n",
       "    <button onclick=\"animPKLBSUBQGAJRUKQL.previous_frame()\"><img class=\"anim_icon\" src=\"\"></button>\n",
       "    <button onclick=\"animPKLBSUBQGAJRUKQL.reverse_animation()\"><img class=\"anim_icon\" src=\"\"></button>\n",
       "    <button onclick=\"animPKLBSUBQGAJRUKQL.pause_animation()\"><img class=\"anim_icon\" src=\"\"></button>\n",
       "    <button onclick=\"animPKLBSUBQGAJRUKQL.play_animation()\"><img class=\"anim_icon\" src=\"\"></button>\n",
       "    <button onclick=\"animPKLBSUBQGAJRUKQL.next_frame()\"><img class=\"anim_icon\" src=\"\"></button>\n",
       "    <button onclick=\"animPKLBSUBQGAJRUKQL.last_frame()\"><img class=\"anim_icon\" src=\"\"></button>\n",
       "    <button onclick=\"animPKLBSUBQGAJRUKQL.faster()\">+</button>\n",
       "  <form action=\"#n\" name=\"_anim_loop_selectPKLBSUBQGAJRUKQL\" class=\"anim_control\">\n",
       "    <input type=\"radio\" name=\"state\" value=\"once\" > Once </input>\n",
       "    <input type=\"radio\" name=\"state\" value=\"loop\" checked> Loop </input>\n",
       "    <input type=\"radio\" name=\"state\" value=\"reflect\" > Reflect </input>\n",
       "  </form>\n",
       "</div>\n",
       "\n",
       "\n",
       "<script language=\"javascript\">\n",
       "  /* Instantiate the Animation class. */\n",
       "  /* The IDs given should match those used in the template above. */\n",
       "  (function() {\n",
       "    var img_id = \"_anim_imgPKLBSUBQGAJRUKQL\";\n",
       "    var slider_id = \"_anim_sliderPKLBSUBQGAJRUKQL\";\n",
       "    var loop_select_id = \"_anim_loop_selectPKLBSUBQGAJRUKQL\";\n",
       "    var frames = new Array(0);\n",
       "    \n",
       "  frames[0] = \"\"\n",
       "  frames[1] = \"\"\n",
       "  frames[2] = \"\"\n",
       "  frames[3] = \"\"\n",
       "  frames[4] = \"\"\n",
       "  frames[5] = \"\"\n",
       "  frames[6] = \"\"\n",
       "  frames[7] = \"\"\n",
       "  frames[8] = \"\"\n",
       "  frames[9] = \"\"\n",
       "  frames[10] = \"\"\n",
       "  frames[11] = \"\"\n",
       "  frames[12] = \"\"\n",
       "  frames[13] = \"\"\n",
       "  frames[14] = \"\"\n",
       "  frames[15] = \"\"\n",
       "  frames[16] = \"\"\n",
       "  frames[17] = \"\"\n",
       "  frames[18] = \"\"\n",
       "  frames[19] = \"\"\n",
       "  frames[20] = \"\"\n",
       "  frames[21] = \"\"\n",
       "  frames[22] = \"\"\n",
       "  frames[23] = \"\"\n",
       "  frames[24] = \"\"\n",
       "  frames[25] = \"\"\n",
       "  frames[26] = \"\"\n",
       "  frames[27] = \"\"\n",
       "  frames[28] = \"\"\n",
       "  frames[29] = \"\"\n",
       "  frames[30] = \"\"\n",
       "  frames[31] = \"\"\n",
       "  frames[32] = \"\"\n",
       "  frames[33] = \"\"\n",
       "  frames[34] = \"\"\n",
       "  frames[35] = \"\"\n",
       "  frames[36] = \"\"\n",
       "  frames[37] = \"\"\n",
       "  frames[38] = \"\"\n",
       "  frames[39] = \"\"\n",
       "  frames[40] = \"\"\n",
       "  frames[41] = \"\"\n",
       "  frames[42] = \"\"\n",
       "  frames[43] = \"\"\n",
       "  frames[44] = \"\"\n",
       "  frames[45] = \"\"\n",
       "  frames[46] = \"\"\n",
       "  frames[47] = \"\"\n",
       "  frames[48] = \"\"\n",
       "  frames[49] = \"\"\n",
       "  frames[50] = \"\"\n",
       "  frames[51] = \"\"\n",
       "  frames[52] = \"\"\n",
       "  frames[53] = \"\"\n",
       "  frames[54] = \"\"\n",
       "  frames[55] = \"\"\n",
       "  frames[56] = \"\"\n",
       "  frames[57] = \"\"\n",
       "  frames[58] = \"\"\n",
       "  frames[59] = \"\"\n",
       "  frames[60] = \"\"\n",
       "  frames[61] = \"\"\n",
       "  frames[62] = \"\"\n",
       "  frames[63] = \"\"\n",
       "  frames[64] = \"\"\n",
       "  frames[65] = \"\"\n",
       "  frames[66] = \"\"\n",
       "  frames[67] = \"\"\n",
       "  frames[68] = \"\"\n",
       "  frames[69] = \"\"\n",
       "  frames[70] = \"\"\n",
       "  frames[71] = \"\"\n",
       "  frames[72] = \"\"\n",
       "  frames[73] = \"\"\n",
       "  frames[74] = \"\"\n",
       "  frames[75] = \"\"\n",
       "  frames[76] = \"\"\n",
       "  frames[77] = \"\"\n",
       "  frames[78] = \"\"\n",
       "  frames[79] = \"\"\n",
       "  frames[80] = \"\"\n",
       "  frames[81] = \"\"\n",
       "  frames[82] = \"\"\n",
       "  frames[83] = \"\"\n",
       "  frames[84] = \"\"\n",
       "  frames[85] = \"\"\n",
       "  frames[86] = \"\"\n",
       "  frames[87] = \"\"\n",
       "  frames[88] = \"\"\n",
       "  frames[89] = \"\"\n",
       "  frames[90] = \"\"\n",
       "  frames[91] = \"\"\n",
       "  frames[92] = \"\"\n",
       "  frames[93] = \"\"\n",
       "  frames[94] = \"\"\n",
       "  frames[95] = \"\"\n",
       "  frames[96] = \"\"\n",
       "  frames[97] = \"\"\n",
       "  frames[98] = \"\"\n",
       "  frames[99] = \"\"\n",
       "  frames[100] = \"\"\n",
       "  frames[101] = \"\"\n",
       "  frames[102] = \"\"\n",
       "  frames[103] = \"\"\n",
       "  frames[104] = \"\"\n",
       "  frames[105] = \"\"\n",
       "  frames[106] = \"\"\n",
       "  frames[107] = \"\"\n",
       "  frames[108] = \"\"\n",
       "  frames[109] = \"\"\n",
       "  frames[110] = \"\"\n",
       "  frames[111] = \"\"\n",
       "  frames[112] = \"\"\n",
       "  frames[113] = \"\"\n",
       "  frames[114] = \"\"\n",
       "  frames[115] = \"\"\n",
       "  frames[116] = \"\"\n",
       "  frames[117] = \"\"\n",
       "  frames[118] = \"\"\n",
       "  frames[119] = \"\"\n",
       "  frames[120] = \"\"\n",
       "  frames[121] = \"\"\n",
       "  frames[122] = \"\"\n",
       "  frames[123] = \"\"\n",
       "  frames[124] = \"\"\n",
       "  frames[125] = \"\"\n",
       "  frames[126] = \"\"\n",
       "  frames[127] = \"\"\n",
       "  frames[128] = \"\"\n",
       "  frames[129] = \"\"\n",
       "  frames[130] = \"\"\n",
       "  frames[131] = \"\"\n",
       "  frames[132] = \"\"\n",
       "  frames[133] = \"\"\n",
       "  frames[134] = \"\"\n",
       "  frames[135] = \"\"\n",
       "  frames[136] = \"\"\n",
       "  frames[137] = \"\"\n",
       "  frames[138] = \"\"\n",
       "  frames[139] = \"\"\n",
       "  frames[140] = \"\"\n",
       "  frames[141] = \"\"\n",
       "  frames[142] = \"\"\n",
       "  frames[143] = \"\"\n",
       "  frames[144] = \"\"\n",
       "  frames[145] = \"\"\n",
       "  frames[146] = \"\"\n",
       "  frames[147] = \"\"\n",
       "  frames[148] = \"\"\n",
       "  frames[149] = \"\"\n",
       "  frames[150] = \"\"\n",
       "  frames[151] = \"\"\n",
       "  frames[152] = \"\"\n",
       "  frames[153] = \"\"\n",
       "  frames[154] = \"\"\n",
       "  frames[155] = \"\"\n",
       "  frames[156] = \"\"\n",
       "  frames[157] = \"\"\n",
       "  frames[158] = \"\"\n",
       "  frames[159] = \"\"\n",
       "  frames[160] = \"\"\n",
       "  frames[161] = \"\"\n",
       "  frames[162] = \"\"\n",
       "  frames[163] = \"\"\n",
       "  frames[164] = \"\"\n",
       "  frames[165] = \"\"\n",
       "  frames[166] = \"\"\n",
       "  frames[167] = \"\"\n",
       "  frames[168] = \"\"\n",
       "  frames[169] = \"\"\n",
       "  frames[170] = \"\"\n",
       "  frames[171] = \"\"\n",
       "  frames[172] = \"\"\n",
       "  frames[173] = \"\"\n",
       "  frames[174] = \"\"\n",
       "  frames[175] = \"\"\n",
       "  frames[176] = \"\"\n",
       "  frames[177] = \"\"\n",
       "  frames[178] = \"\"\n",
       "  frames[179] = \"\"\n",
       "  frames[180] = \"\"\n",
       "  frames[181] = \"\"\n",
       "  frames[182] = \"\"\n",
       "  frames[183] = \"\"\n",
       "  frames[184] = \"\"\n",
       "  frames[185] = \"\"\n",
       "  frames[186] = \"\"\n",
       "  frames[187] = \"\"\n",
       "  frames[188] = \"\"\n",
       "  frames[189] = \"\"\n",
       "  frames[190] = \"\"\n",
       "  frames[191] = \"\"\n",
       "  frames[192] = \"\"\n",
       "  frames[193] = \"\"\n",
       "  frames[194] = \"\"\n",
       "  frames[195] = \"\"\n",
       "  frames[196] = \"\"\n",
       "  frames[197] = \"\"\n",
       "  frames[198] = \"\"\n",
       "  frames[199] = \"\"\n",
       "\n",
       "\n",
       "    /* set a timeout to make sure all the above elements are created before\n",
       "       the object is initialized. */\n",
       "    setTimeout(function() {\n",
       "        animPKLBSUBQGAJRUKQL = new Animation(frames, img_id, slider_id, 50, loop_select_id);\n",
       "    }, 0);\n",
       "  })()\n",
       "</script>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Imports specifically so we can render outputs in Colab.\n",
    "from matplotlib import animation\n",
    "from JSAnimation.IPython_display import display_animation\n",
    "from IPython.display import display\n",
    "\n",
    "\n",
    "def display_frames_as_gif(frames):\n",
    "    \"\"\"Displays a list of frames as a gif, with controls.\"\"\"\n",
    "    patch = plt.imshow(frames[0])\n",
    "    plt.axis('off')\n",
    "\n",
    "    def animate(i):\n",
    "        patch.set_data(frames[i])\n",
    "\n",
    "    anim = animation.FuncAnimation(\n",
    "        plt.gcf(), animate, frames = len(frames), interval=50\n",
    "    )\n",
    "    display(display_animation(anim, default_mode='loop'))\n",
    "    \n",
    "        \n",
    "# display \n",
    "display_frames_as_gif(frames)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
